package jfuzzyprincepless.zarchive.negotiation;

import jfuzzyprincepless.negotiation.INegotiationContext;
import jfuzzyprincepless.negotiation.NegotiationUtils;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.fuzzyfor.INegotiationFuzzyFor;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.BaseNegotiationFor;
import jfuzzyprincepless.zarchive.algorithm.structure.ifcnstr.NegotiationIfConstruction;
import jfuzzyprincepless.zarchive.negotiation.protocols.INegotiationProtocol__Q_del;
import jfuzzyprincepless.zarchive.negotiation.protocols.FrontNegotiationProtocol;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.DefaultExecForProtocol;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.DefaultExecIfProtocol;
import jfuzzyprincepless.zarchive.negotiation.services.IExecuteForService;
import jfuzzyprincepless.algorithm.structure.IConstruction;
import jfuzzyprincepless.agents.INegotiatingAgent;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

public class ZArchNegotiationUtils {
	public static final int CMD_IS_AGENT = (1 << 0);
	public static final int CMD_IS_EXPORTER = (1 << 1);

	/**
	 * casts negotiation.getForConstruction() to INegotiationFuzzyFor
	 * @throws NullPointerException if service IExecuteForService<E>
	 * does not exists
	 * @throws ClassCastException if forService.getForConstruction()
	 * is not INegotiationFuzzyFor
	 */
	@SuppressWarnings("unchecked")
	public static <E> double getCurrentMembershipDegree( //!!!
		INegotiationContext context
	) {
		IExecuteForService<E> forService =
			context.getService(IExecuteForService.class);
		return getMembershipDegree(
			forService.getCurrentElem(),
			(INegotiationFuzzyFor<E>)forService.getForConstruction()
		);
	}

	private static <E> double getMembershipDegree( //!!!
		E item,
		INegotiationFuzzyFor<E> fuzzyFor
	) {
		return fuzzyFor.getFuzzyForSet()
			.getMembershipDegree(item);
	}

	@SuppressWarnings("unchecked")
	public static INegotiationProtocol__Q_del newDefaultNegotiationProtocol() { //!!!
		FrontNegotiationProtocol protocol =
			new FrontNegotiationProtocol();
		protocol.specifyForType(
			BaseNegotiationFor.class,
			new DefaultExecForProtocol(),
			true
		);
		protocol.specifyForType(
			NegotiationIfConstruction.class,
			new DefaultExecIfProtocol()
		);
		return protocol;
	}

	public static void addAgents(
		List<? extends IConstruction> constructions,
		List<INegotiatingAgent> destination,
		int extractFlags
	) {
		boolean cmdIsAgent = (extractFlags & CMD_IS_AGENT) != 0;
		boolean cmdIsExporter = (extractFlags & CMD_IS_EXPORTER) != 0;
		for (IConstruction constrElem : constructions) {
			if (
				constrElem instanceof INegotiatingAgent &&
				cmdIsAgent
			) {
				destination.add((INegotiatingAgent) constrElem);
			}
			if (
				constrElem instanceof IAgentsExporter &&
				cmdIsExporter
			) {
				IAgentsExporter agentsExporter =
					(IAgentsExporter) constrElem;
				destination.addAll(agentsExporter.getAgents());
			}
		}
	}

	public static List<INegotiatingAgent> extractAgents(
		List<? extends IConstruction> constructions,
		int extractFlags
	) {
		List<INegotiatingAgent> agentList =
			new ArrayList<INegotiatingAgent>();
		addAgents(constructions, agentList, extractFlags);
		return agentList;
	}

	public static void defaultNegotiationIteration(
			IAgentsExporter aexp,
			INegotiationContext context
	) {
		NegotiationUtils.defaultNegotiationIteration(aexp.getAgents(), context);
	}

	public static List<INegotiatingAgent> extractAgents(
		IConstruction ... constructions
	) {
		return ZArchNegotiationUtils.extractAgents(Arrays.asList(constructions), CMD_IS_AGENT);
	}

	public static List<INegotiatingAgent> extractAgents(
		List<? extends IConstruction> constructions
	) {
		return ZArchNegotiationUtils.extractAgents(
			constructions,
			CMD_IS_AGENT | CMD_IS_EXPORTER
		);
	}

	public static List<INegotiatingAgent> extractAgents(
		int extractFlags,
		List<? extends IConstruction> ... constructions
	) {
		List<INegotiatingAgent> agentList =
			new ArrayList<INegotiatingAgent>();
		for (
			List<? extends IConstruction> constrList :
			constructions
		) {
			addAgents(constrList, agentList, extractFlags);
		}
		return agentList;
	}

	public static List<INegotiatingAgent> extractAgents(
		List<? extends IConstruction> ... constructions
	) {
		return ZArchNegotiationUtils.extractAgents(
			CMD_IS_AGENT | CMD_IS_EXPORTER,
			constructions
		);
	}
}
